package gaozhi.online.parent.privilege;

import gaozhi.online.parent.component.GetBeanHelper;
import gaozhi.online.parent.exception.BusinessRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * 权限监听器
 * 在应用启动时扫描所有带@Privilege注解的接口
 *
 * @author http://gaozhi.online
 * @version 1.0
 * @since 2022-11-21
 */
@Slf4j
public abstract class PrivilegesListener implements ApplicationRunner {
    /**
     * 模块名称
     */
    @Value("${spring.application.name}")
    private String moduleName;

    private final GetBeanHelper getBeanHelper;

    public PrivilegesListener(GetBeanHelper getBeanHelper) {
        this.getBeanHelper = getBeanHelper;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        //前置处理
        preHandle(moduleName);
        log.info("扫描需要鉴权的Api-------------------------------------------------------------------------------");
        RequestMappingHandlerMapping mapping = getBeanHelper.getBean(RequestMappingHandlerMapping.class);
        // 拿到Handler适配器中的全部方法
        Map<RequestMappingInfo, HandlerMethod> methodMap = mapping.getHandlerMethods();

        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : methodMap.entrySet()) {
            HandlerMethod handlerMethod = entry.getValue();
            Privilege classPrivilege = handlerMethod.getBeanType().getAnnotation(Privilege.class);
            if (classPrivilege == null) {
                log.debug("类没有注解---{}", handlerMethod.getBeanType().getName());
                continue;
            }
            Privilege methodPrivilege = handlerMethod.getMethodAnnotation(Privilege.class);
            if (methodPrivilege == null) {
                log.debug("方法没有注解---{}", handlerMethod.getMethod().getName());
                continue;
            }
            RequestMapping classRequest = handlerMethod.getBeanType().getAnnotation(RequestMapping.class);
            if (classRequest == null) {
                throw new BusinessRuntimeException(BusinessRuntimeException.ServerExceptionEnum.API_ERROR, "如果使用Privilege注解，类必须添加RequestMapping注解");
            }
            String className = classRequest.name();
            String[] classPaths = classRequest.path();
            String methodName = "";
            String[] methodPaths = {};
            RequestMethod[] types = {};
            Method method = handlerMethod.getMethod();
            
            // 优化HTTP方法处理逻辑
            if (method.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping methodRequest = method.getAnnotation(RequestMapping.class);
                methodName = methodRequest.name();
                methodPaths = methodRequest.path();
                types = methodRequest.method();
            } else {
                // 统一处理各种HTTP方法映射注解
                types = getRequestMethodTypes(method);
                methodPaths = getRequestMethodPaths(method);
                methodName = getRequestMethodName(method);
            }

            for (RequestMethod type : types) {
                for (String classPath : classPaths) {
                    for (String methodPath : methodPaths) {
                        //处理权限
                        handlePrivilege(moduleName, className, methodName, classPath, methodPath, type, methodPrivilege.isPublic());
                        //兼容旧版本
                        handlePrivilege(moduleName, className, methodName, classPath, methodPath, type);
                    }
                }
            }
        }
        afterHandle(moduleName);
    }

    /**
     * 获取方法的HTTP请求类型
     * @param method 方法
     * @return 请求类型数组
     */
    private RequestMethod[] getRequestMethodTypes(Method method) {
        if (method.isAnnotationPresent(GetMapping.class)) {
            return new RequestMethod[]{RequestMethod.GET};
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            return new RequestMethod[]{RequestMethod.POST};
        } else if (method.isAnnotationPresent(PutMapping.class)) {
            return new RequestMethod[]{RequestMethod.PUT};
        } else if (method.isAnnotationPresent(PatchMapping.class)) {
            return new RequestMethod[]{RequestMethod.PATCH};
        } else if (method.isAnnotationPresent(DeleteMapping.class)) {
            return new RequestMethod[]{RequestMethod.DELETE};
        }
        // 默认返回GET
        return new RequestMethod[]{RequestMethod.GET};
    }

    /**
     * 获取方法的请求路径
     * @param method 方法
     * @return 请求路径数组
     */
    private String[] getRequestMethodPaths(Method method) {
        if (method.isAnnotationPresent(GetMapping.class)) {
            return method.getAnnotation(GetMapping.class).path();
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            return method.getAnnotation(PostMapping.class).path();
        } else if (method.isAnnotationPresent(PutMapping.class)) {
            return method.getAnnotation(PutMapping.class).path();
        } else if (method.isAnnotationPresent(PatchMapping.class)) {
            return method.getAnnotation(PatchMapping.class).path();
        } else if (method.isAnnotationPresent(DeleteMapping.class)) {
            return method.getAnnotation(DeleteMapping.class).path();
        }
        // 默认返回空数组
        return new String[0];
    }

    /**
     * 获取方法的名称
     * @param method 方法
     * @return 方法名称
     */
    private String getRequestMethodName(Method method) {
        if (method.isAnnotationPresent(GetMapping.class)) {
            return method.getAnnotation(GetMapping.class).name();
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            return method.getAnnotation(PostMapping.class).name();
        } else if (method.isAnnotationPresent(PutMapping.class)) {
            return method.getAnnotation(PutMapping.class).name();
        } else if (method.isAnnotationPresent(PatchMapping.class)) {
            return method.getAnnotation(PatchMapping.class).name();
        } else if (method.isAnnotationPresent(DeleteMapping.class)) {
            return method.getAnnotation(DeleteMapping.class).name();
        }
        // 默认返回空字符串
        return "";
    }

    /**
     * 后置处理
     *
     * @param moduleName 模块名称
     */
    protected abstract void afterHandle(String moduleName);
    /**
     * 处理权限
     *
     * @param moduleName  模块名称
     * @param className   类名
     * @param methodName  方法名
     * @param classPath   类路径
     * @param methodPath  方法路径
     * @param method      请求方法
     */
    protected void handlePrivilege(String moduleName, String className, String methodName,
                                            String classPath, String methodPath, RequestMethod method){

    }

    /**
     * 处理权限
     *
     * @param moduleName  模块名称
     * @param className   类名
     * @param methodName  方法名
     * @param classPath   类路径
     * @param methodPath  方法路径
     * @param method      请求方法
     * @param isPublic    是否公共接口
     */
    protected void handlePrivilege(String moduleName, String className, String methodName,
                                          String classPath, String methodPath, RequestMethod method,boolean isPublic){

    }

    /**
     * 前置处理
     *
     * @param moduleName 模块名称
     */
    protected abstract void preHandle(String moduleName);
}
